package com.hxgis.oc.product.base;

import com.hxgis.oc.config.OConfig;
import com.hxgis.oc.util.ds.FileDatabase;
import com.hxgis.oc.util.ds.FileDatabaseManager;
import com.hxgis.oc.util.ds.FileRecord;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
public abstract class ProductFactory {
    @Getter
    private final String productType;

    private final int numThreadWorking;

    public ProductFactory(String name,String[] acceptDataName,int numThread) {
        numThreadWorking = numThread;
        productType = name;
        Collections.addAll(acceptMap, acceptDataName);
        ProductFactoryManager.add(name,this);
    }

    protected abstract void onPublish(ProduceTask o);
    protected abstract void onRetrieve(ProduceTask o);

    protected HashSet<String> acceptMap = new HashSet<>();
    public boolean accept(String dataName){
        return acceptMap.contains(dataName);
    }

    private FileDatabaseManager fileDatabaseManager;

    public void registerProduct(String name){
        if(!accept(name)){
            return;
        }

        if(fileDatabaseManager == null){
            fileDatabaseManager = new FileDatabaseManager();
        }

        String productType = getProductType();

        FileDatabase fileDatabase;

        String root = new File(OConfig.getConfig().getDataDir()).getAbsolutePath() +
                File.separator + productType;
        if(!new File(root).exists()){
            new File(root).mkdir();
        }

        String databaseFileName = root + File.separator + name + ".db";
        try {
            fileDatabase = fileDatabaseManager.add(name,databaseFileName);
            fileDatabase.createTable(name);
            new File(root + File.separator + name).mkdir();
        } catch (Exception e) {
            log.error(e.getMessage());
        }

    }

    private ProductFactoryWorker worker;
    public ProductFactoryWorker getWorker() {
        return worker == null ? worker = new ProductFactoryWorker(numThreadWorking) : worker;
    }

    protected FileDatabase getFileDatabase(String dataSet) {
        return fileDatabaseManager == null ? null : fileDatabaseManager.get(dataSet);
    }

    protected void submit(ProduceTask task) {
        String dataSet = task.getDataSet();
        long fid = task.getFileID();

        FileDatabase fileDatabase = getFileDatabase(dataSet);
        if(fileDatabase == null){
            return;
        }

        String fileName = task.getOutputFile().getName();
        if(!fileDatabase.exist(dataSet,fid)){
            fileDatabase.insert(dataSet,fid,fileName);
        }
    }

    protected void addConvTask(ConvertTask task,String fileName) {
        String dataSet = task.getDataSet();
        long fid = task.getFileID();

        FileDatabase fileDatabase = getFileDatabase(dataSet);
        if(fileDatabase != null && !fileDatabase.exist(dataSet,fid)){
            task.setOutputFile(fileDatabase.getFile(dataSet,fileName));

            if(task.isAsync()){
                getWorker().addTask(task);
            }else{
                task.run();
            }
        }
    }

    public void clearInvalids(String dataSet, long clearTimeStamp) {
        clearInvalid(dataSet);
        clearExpired(dataSet,clearTimeStamp);
    }

    protected void clearInvalid(String dataSet) {
        FileDatabase fileDatabase = getFileDatabase(dataSet);
        if(fileDatabase == null){
            return;
        }
        List<FileRecord> invalidRecords = new ArrayList<>();

        List<FileRecord> list = fileDatabase.selectAll(dataSet);
        for(FileRecord record : list)
        {
            File file = fileDatabase.getFile(dataSet,record.getFileName());
            if(!file.exists()){
                invalidRecords.add(record);
            }
        }

        if(invalidRecords.size() > 0){
            fileDatabase.deleteInvalid(dataSet, invalidRecords);
        }
    }

    protected void clearExpired(String dataSet, long expiredTime) {
        if(expiredTime == 0){
            return;
        }

        FileDatabase fileDatabase = getFileDatabase(dataSet);

        List<FileRecord> list = fileDatabase.select(dataSet,null,expiredTime);
        for(FileRecord record : list)
        {
            File file = fileDatabase.getFile(dataSet,record.getFileName());
            file.delete();
        }

        fileDatabase.deleteExpired(dataSet, expiredTime);
    }

    public boolean exist(String dataSet, long fileID) {
        FileDatabase fileDatabase = getFileDatabase(dataSet);
        return fileDatabase != null && fileDatabase.exist(dataSet,fileID);
    }

    public int count(String dataSet) {
        return count(dataSet, (Long)null);
    }

    public int count(String dataSet, LocalDateTime startTime) {
        return count(dataSet,OConfig.genTimeStampFid(startTime));
    }

    public int count(String dataSet,Long startTime) {
        FileDatabase fileDatabase = getFileDatabase(dataSet);
        return fileDatabase==null ? 0 : fileDatabase.count(dataSet,startTime);
    }

    public File getFile(String dataSet, Long fileID) {
        FileDatabase fileDatabase = getFileDatabase(dataSet);
        return fileDatabase==null ? null : fileDatabase.getFile(dataSet,fileID);
    }

    public File getFile(String dataSet, String fileName) {
        FileDatabase fileDatabase = getFileDatabase(dataSet);
        return fileDatabase==null ? null : fileDatabase.getFile(dataSet,fileName);
    }

    public List<FileRecord> getFileList(String dataSet) {
        return getFileList(dataSet,null,null);
    }

    public List<FileRecord> getFileList(String dataSet, Long startTime, Long endTime) {
        FileDatabase fileDatabase = getFileDatabase(dataSet);
        return fileDatabase==null ? Collections.emptyList() : fileDatabase.select(dataSet,startTime,endTime);
    }
}
